package entity;

import game.util.MatrixUtil;

import java.util.HashSet;
import java.util.Set;

import model.Model;

import org.lwjgl.util.vector.Matrix3f;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

/**
 * 
 * @author Jevgeni Krutov
 *
 */
public class Entity {
	protected Vector3f position;
	protected Vector3f rotation;
	
	public Model model;
	protected Matrix4f matrix;
	private Vector3f xAxis, yAxis, zAxis;
	protected boolean isModelMatrixValid = false;
	private Vector3f centerPoint;
	
	public Entity (Vector3f position, Vector3f rotation) {
		this.position = position;
		this.rotation = rotation;
		
		xAxis = new Vector3f(1, 0, 0);
		yAxis = new Vector3f(0, 1, 0);
		zAxis = new Vector3f(0, 0, 1);
		
		setMatrix(MatrixUtil.createIdentityMatrix());
		centerPoint = recalculateCenterPoint();
		
		setMass(500f);
	}
	
	
	//methods to work with model matrix
	
	public void setMatrix(Matrix4f modelMatrix) {
		this.matrix = modelMatrix;
	}
	
	protected void recalculateMatrix() {
		matrix.setIdentity();
//		Matrix4f.scale(scale, modelMatrix, modelMatrix);//scaling not needed yet
		Matrix4f.translate(position, matrix, matrix);
		Matrix4f.rotate((float) Math.toRadians(rotation.z), zAxis, matrix, matrix);
		Matrix4f.rotate((float) Math.toRadians(rotation.y), yAxis, matrix, matrix);
		Matrix4f.rotate((float) Math.toRadians(rotation.x), xAxis, matrix, matrix);
		isModelMatrixValid = true;
		
		recalculateCenterPoint();
	}
	
	public Matrix4f getMatrix() {
		if(!isModelMatrixValid) {
			recalculateMatrix();
		}
		return matrix;
	}
	
	public void invalidateMatrix() {
		isModelMatrixValid = false;
	}
	
	
	//Methods to be overridden by moving entities only
	
	/**
	 * method to override that handles response to player's input
	 */
	public void listenToActionAndMove(long elapsedTime) {};
	
	public void addPosition(Vector3f position) {}
	
	public void addPosition(float x, float y, float z) {}
	
	public void addRotation(Vector3f rotation) {}
	
	public void addRotation(float rx, float ry, float rz) {}
	
	protected void moveInDirection(float amount, float direction) {}
	
	public void applyForce(Vector3f force) {}
	
	private Vector3f nullVector = new Vector3f(0, 0, 0);
	
	public void changeSpeedAndMove(long elapsedTime) {}
	
	public Vector3f getSpeed() {
		return nullVector;
	}
	
	public void setSpeed(Vector3f speed) {}
	
	public float getMass() {
		return 0f;
	}
	
	public void setMass(float mass) {}

	
	// other methods to be optionally overridden
	
	public void decreaseAwaitingTimes(long elapsedTime) {}
	
	protected long getDecreasedAwaitingTime(long elapsedTime, long someAwaitingTime) {
		if(someAwaitingTime <= 0) {
			return 0;
		}
		someAwaitingTime -= elapsedTime;
		return someAwaitingTime;
	}
	
	public boolean isPlayer() {
		return false;
	}
	
	public boolean isMovingEntity() {
		return false;
	}
	
	public boolean isCharacter() {
		return false;
	}
	
	public boolean isShot() {
		return false;
	}
	
	public boolean isGround() {
		return false;
	}
	
	public boolean isItem() {
		return false;
	}
	
	public boolean isAlive() {
		return true;
	}
	
	/**
	 * 
	 * @return center point of a model according to its BoundingBox
	 */
	public Vector3f getCenterPoint() {
		if(model == null) {
			return position;
		}
		return centerPoint;
	}
	
	private Vector3f recalculateCenterPoint() {
		if(model == null) {
			return position;
		}
		
		Vector3f centerPoint = new Vector3f();
		
		Vector3f centerOffsetVector = new Vector3f();
		Matrix3f model3f = MatrixUtil.getM3f(getMatrix());
		Matrix3f.transform(model3f, model.getBoundingBox().getCenterOffset(), centerOffsetVector);
		Vector3f.add(position, centerOffsetVector, centerPoint);
		
		this.centerPoint = centerPoint;
		return centerPoint;
	}
	
	
	// basic getters and setters below
	
	public Vector3f getPosition() {
		return position;
	}
	
	public void setPosition(Vector3f position) {
		this.position = position;
		invalidateMatrix();
	}
	
	public void setPosition(float x, float y, float z) {
		position.x = x;
		position.y = y;
		position.z = z;
		invalidateMatrix();
	}

	public Vector3f getRotation() {
		return rotation;
	}

	public void setRotation(Vector3f rotation) {
		this.rotation = rotation;
		invalidateMatrix();
	}

	public void setRotation(float rx, float ry, float rz) {
		rotation.x = rx;
		rotation.y = ry;
		rotation.z = rz;
		invalidateMatrix();
	}

	public Model getModel() {
		return model;
	}
	
	public Entity setModel(Model model) {
		this.model = model;
		return this;
	}

	public void render() {
		this.model.render();
	}
}
